#ifndef EXPR_H
#define EXPR_H

/* an expression can be evaluated to return an object of the
 * expression's type */

typedef enum expr_type
{
  EXPR_TYPE_BOOLEAN,
  EXPR_TYPE_NATURAL,
  EXPR_TYPE_REAL,
  EXPR_TYPE_RkVECTOR,
  EXPR_TYPE_STRING,
  EXPR_TYPE_IDENTIFIER,
  EXPR_TYPE_FN_CALL,
  EXPR_TYPE_CPD_CALL,
  EXPR_TYPE_IF,                                  /* if statement */
  EXPR_TYPE_SET_IMP,                             /* implicit set of objects */
  EXPR_TYPE_SET_EXP,                             /* explicit set of objects */
  EXPR_TYPE_SET_CARD,                            /* cardinality of a set */
  EXPR_TYPE_DEP,                                 /* dependency */
  EXPR_TYPE_EQUAL,
  EXPR_TYPE_NULL
} expr_type;

typedef enum expr_rettype
{
  EXPR_RETTYPE_BOOLEAN,
  EXPR_RETTYPE_NATURAL,
  EXPR_RETTYPE_REAL,
  EXPR_RETTYPE_RkVECTOR,
  EXPR_RETTYPE_STRING,
  EXPR_RETTYPE_OBJECT,
  EXPR_RETTYPE_SET,
  EXPR_RETTYPE_NULL
} expr_rettype;

typedef struct expr_rkvector
{
  int       nelems_expr_rkvector;
  struct expr   ** ppelems_expr_rkvector;
} expr_rkvector;

typedef struct expr_fn_call
{
  char *    fnname_expr_fn_call;
  int       nargs_expr_fn_call;
  struct expr **   ppargs_expr_fn_call;
} expr_fn_call;

typedef struct expr_cpd_call
{
  char *    fnname_expr_cpd_call;
  int       nparams_expr_cpd_call;
  struct expr **   ppparms_expr_cpd_call;
  int       nargs_expr_cpd_call;
  struct expr **   ppargs_expr_cpd_call;
} expr_cpd_call;

typedef struct expr_if
{
  struct expr * condition_expr_if;
  struct expr * then_expr_if;                /* if the condition is true */
  struct expr * elseif_expr_if;              /* next if condition */
} expr_if;

typedef struct expr_seti
{
  char          * typename_expr_seti;
  struct expr   * condition_expr_seti;
} expr_seti;

typedef struct expr_sete
{
  int             nelems_expr_sete;
  struct expr  ** ppelems_expr_sete;
} expr_sete;

typedef struct expr_setc
{
  struct expr   * set_expr_setc;
} expr_setc;

typedef struct expr_dep
{
  int            is_det_expr_dep;            /* is deterministic? */
  struct expr *  rhs_expr_dep;
} expr_dep;

typedef struct expr_equal
{
  struct expr * lhs_expr_equal;
  struct expr * rhs_expr_equal;
} expr_equal;

typedef union expr_val
{
  int             bool_expr_val;
  int             natural_expr_val;
  double          real_expr_val;
  expr_rkvector   rkvector_expr_val;
  char          * string_expr_val;
  char          * ident_expr_val;
  expr_fn_call    fn_call_expr_val;
  expr_cpd_call   cpd_call_expr_val;
  expr_if         if_expr_val;
  expr_seti       seti_expr_val;
  expr_sete       sete_expr_val;
  expr_setc       setc_expr_val;
  expr_dep        dep_expr_val;
  expr_equal      equal_expr_val;
} expr_val;

typedef struct expr
{
  expr_type  type_expr;
  expr_val   val_expr;
  
  /* shortcuts */
#define bool_expr_val          val_expr.bool_expr_val
#define natural_expr_val       val_expr.natural_expr_val
#define real_expr_val          val_expr.real_expr_val
#define nelems_expr_rkvector   val_expr.rkvector_expr_val.nelems_expr_rkvector
#define ppelems_expr_rkvector  val_expr.rkvector_expr_val.ppelems_expr_rkvector
#define string_expr_val        val_expr.string_expr_val
#define ident_expr_val         val_expr.ident_expr_val
#define fnname_expr_fn_call    val_expr.fn_call_expr_val.fnname_expr_fn_call
#define nargs_expr_fn_call     val_expr.fn_call_expr_val.nargs_expr_fn_call
#define ppargs_expr_fn_call    val_expr.fn_call_expr_val.ppargs_expr_fn_call 
#define fnname_expr_cpd_call   val_expr.cpd_call_expr_val.fnname_expr_cpd_call
#define nparams_expr_cpd_call  val_expr.cpd_call_expr_val.nparams_expr_cpd_call
#define ppparms_expr_cpd_call  val_expr.cpd_call_expr_val.ppparms_expr_cpd_call
#define nargs_expr_cpd_call    val_expr.cpd_call_expr_val.nargs_expr_cpd_call
#define ppargs_expr_cpd_call   val_expr.cpd_call_expr_val.ppargs_expr_cpd_call
#define condition_expr_if      val_expr.if_expr_val.condition_expr_if
#define then_expr_if           val_expr.if_expr_val.then_expr_if
#define elseif_expr_if         val_expr.if_expr_val.elseif_expr_if
#define typename_expr_seti     val_expr.seti_expr_val.typename_expr_seti
#define condition_expr_seti    val_expr.seti_expr_val.condition_expr_seti
#define nelems_expr_sete       val_expr.sete_expr_val.nelems_expr_sete
#define ppelems_expr_sete      val_expr.sete_expr_val.ppelems_expr_sete
#define set_expr_setc          val_expr.setc_expr_val.set_expr_setc
#define is_det_expr_dep        val_expr.dep_expr_val.is_det_expr_dep
#define rhs_expr_dep           val_expr.dep_expr_val.rhs_expr_dep
#define lhs_expr_equal         val_expr.equal_expr_val.lhs_expr_equal
#define rhs_expr_equal         val_expr.equal_expr_val.rhs_expr_equal
} expr;

typedef struct expr_ret
{
  expr_rettype   type_expr_rettype;
  
} expr_ret;

expr * expr_alloc_boolean(int bool);

expr * expr_alloc_natural(int num);

expr * expr_alloc_real(double val);

/* arglist memory is owned by the expr */
expr * expr_alloc_rkvector(void * arglist);

/* string memory is owned by the expr */
expr * expr_alloc_string(char * string);

/* ident memory is owned by the expr */
expr * expr_alloc_ident(char * ident);

/* the fnname and arglist is owned by the expr */
expr * expr_alloc_fn_call(char * fnname, void * arglist);

/* fnname, arglist and paramlist are owned by expr */
expr * expr_alloc_cpd_call(char * fnname, void *paramlist, void *arglist);

/* if, then, and else are owned by expr */
expr * expr_alloc_if(expr * condition, expr * then, expr * elseif);

/* typename and condition are owned by expression */
expr * expr_alloc_seti(char * typename, expr * condition);

/* arglist is owned by expr */
expr * expr_alloc_sete(void * arglist);

/* set is now owned by the new expression */
expr * expr_alloc_setc(expr * set);

/* rhs is owned by expr */
expr * expr_alloc_dep(int isdeterministic, expr * rhs);

/* lhs, rhs is owned by expr */
expr * expr_alloc_equal(expr * lhs, expr * rhs);

expr * expr_alloc_null(void);


#endif /* EXPR_H */
